package com.nulldev.util.web.HttpServer3.pages.js_backend.impl;

import com.nulldev.util.JVM.reflections.Reflections;
import com.nulldev.util.data.Variables;
import com.nulldev.util.io.IOUtils;
import com.nulldev.util.web.HttpClient.HttpClient;
import com.nulldev.util.web.HttpClient.HttpMethod;
import com.nulldev.util.web.HttpClient.HttpRequest;
import com.nulldev.util.web.HttpClient.HttpResponse;

public class JSXMLHttpRequest {

	public static final JSXMLHttpRequest SERVER_XML = new JSXMLHttpRequest();

	public static class XMLHttpRequest {

		public final int UNSENT = 0;
		public final int OPENED = 1;
		public final int HEADERS_RECEIVED = 2;
		public final int LOADING = 3;
		public final int DONE = 4;

		public short readyState = UNSENT;

		public Object onreadystatechange;
		public int status = 0;
		public String responseText = null;

		private HttpRequest<String> str;
		private HttpMethod m;

		public void open(final String method, final String url) {
			this.open(method, url, false);
		}

		public void open(final String method, final String url, final boolean async) {
			// async is useless for now.
			Variables.requireNonNull(method, "method");
			Variables.requireNonNull("url", url);
			try {
				this.m = HttpMethod.as(method);
				this.str = HttpClient.getString().url(url);
				this.readyState = OPENED;
			} catch (Exception ex) {
				throw new RuntimeException(ex);
			}
		}

		public void send() {
			// Do the magic here.
			Variables.requireNonNull(this.m, "m");
			Variables.requireNonNull(this.str, "null");
			try {
				final boolean shouldGC = this.onreadystatechange == null;
				if (!shouldGC) {
					this.readyState = HEADERS_RECEIVED;
					__invoke(this.onreadystatechange);
					final HttpResponse<String> c = this.str.create(this.m);
					this.status = c.status();
					this.readyState = LOADING;
					__invoke(this.onreadystatechange);
					this.responseText = c.dataThenClose();
					this.readyState = DONE;
					__invoke(this.onreadystatechange);
					IOUtils.closeQuietly(c);
				}
			} catch (Exception ex) {
				throw new RuntimeException(ex);
			}
		}

		private final static Class<?>[] OBJ =
			{ Object.class, Object[].class };
		private final static Object[] ARG_OBJ = new Object[0];

		private void __invoke(final Object obj) {
			try {
				if (Reflections.is("jdk.nashorn.api.scripting.ScriptObjectMirror")
						&& Reflections.get("jdk.nashorn.api.scripting.ScriptObjectMirror").isInstance(obj)) {
					Reflections.encapsulate(obj).invokeDecl("call", OBJ, this, ARG_OBJ);
				} else {
					throw new RuntimeException("Unable to call script function!");
				}
			} catch (Exception ex) {
				throw new RuntimeException(ex);
			}
		}
	}

	public XMLHttpRequest XMLHttpRequest() {
		return new XMLHttpRequest();
	}
}
